﻿// --------------
// 作者:Vitity
// --------------

using NICWebApi.Extensions.DynamicAPI.Options;
using System.Collections.Concurrent;

namespace NICWebApi.Extensions.DynamicAPI.Conventions;

/// <summary>
/// 动态API约定
/// </summary>
public class ApplicationServiceConvention : IApplicationModelConvention, IOrderedFilter
{
    private readonly DynamicApiOptions _options;
    
    // 缓存常用的HTTP方法判断结果
    private static readonly HashSet<string> _queryMethods = new(StringComparer.OrdinalIgnoreCase)
    {
        "GET", "DELETE", "TRACE", "HEAD"
    };
    
    // 缓存简单类型判断结果
    private static readonly ConcurrentDictionary<Type, bool> _simpleTypeCache = new();

    /// <summary>
    /// 获取或设置过滤器的执行顺序
    /// </summary>
    public int Order { get; }

    /// <summary>
    /// 构造函数
    /// </summary>
    public ApplicationServiceConvention()
    {
        // 默认初始化配置选项
        _options = new DynamicApiOptions();
    }

    /// <summary>
    /// 构造函数，使用服务提供者获取配置选项
    /// </summary>
    /// <param name="serviceProvider">服务提供者</param>
    public ApplicationServiceConvention(IServiceProvider serviceProvider)
    {
        // 从服务提供者获取配置选项，如果不存在则使用默认选项
        _options = serviceProvider.GetService<DynamicApiOptions>() ?? Utils.GetOptions();
    }

    /// <summary>
    /// 配置动态API约定
    /// </summary>
    /// <param name="application"></param>
    public void Apply(ApplicationModel application)
    {
        // 遍历所有控制器
        foreach (var controller in application.Controllers)
        {
            // 判断是否动态API
            if (IsDynamicAPI(controller))
            {
                // 配置该控制器
                ConfigureApplicationService(controller);
            }
        }
    }

    /// <summary>
    /// 判断控制器是否为动态API
    /// </summary>
    /// <param name="controller"></param>
    /// <returns></returns>
    private bool IsDynamicAPI(ControllerModel controller)
    {
        if (Utils.IsController(controller.ControllerType))
        {
            return true;
        }
        return false;
    }

    /// <summary>
    /// 判断动作是否为动态API
    /// </summary>
    /// <param name="action"></param>
    /// <returns></returns>
    private bool IsDynamicAction(ActionModel action)
    {
        // 如果动作上定义了DynamicApiAttribute特性
        if (action.Filters.Any(o => o is DynamicApiAttribute))
        {
            return true;
        }
        return false;
    }

    /// <summary>
    /// 动态配置服务
    /// </summary>
    /// <param name="controller"></param>
    private void ConfigureApplicationService(ControllerModel controller)
    {
        // 配置API文档
        ConfigureApiExplorer(controller);
        // 配置选择器
        ConfigureSelector(controller);
    }

    /// <summary>
    /// 配置API文档
    /// </summary>
    /// <param name="controller"></param>
    private void ConfigureApiExplorer(ControllerModel controller)
    {
        // 如果控制器未设置是否可见，则设置为可见
        if (!controller.ApiExplorer.IsVisible.HasValue)
        {
            controller.ApiExplorer.IsVisible = true;
        }

        // 遍历所有动作
        foreach (var action in controller.Actions)
        {
            // 如果动作未设置是否可见，则设置为可见
            if (!action.ApiExplorer.IsVisible.HasValue && IsDynamicAction(action))
            {
                action.ApiExplorer.IsVisible = true;
            }
            else
            {
                action.ApiExplorer.IsVisible = false;
            }
        }
    }

    /// <summary>
    /// 配置选择器
    /// </summary>
    /// <param name="controller"></param>
    private void ConfigureSelector(ControllerModel controller)
    {
        // 遍历所有动作，配置选择器
        foreach (var action in controller.Actions)
        {
            if (IsDynamicAction(action))
            {
                // 配置HttpMethod
                NormalizeSelectorHttpMethod(action);
                // 配置动态路由
                NormalizeSelectorRoutes(action);
            }
        }
    }

    /// <summary>
    /// 标准化路由模板
    /// </summary>
    /// <param name="action"></param>
    private void NormalizeSelectorRoutes(ActionModel action)
    {
        foreach (var selectorModel in action.Selectors)
        {
            // 跳过已配置路由特性的配置
            if (selectorModel.AttributeRouteModel != null)
            {
                // 如果控制器自定义了 [Route] 特性，则跳过
                if (action.ActionMethod.DeclaringType.IsDefined(typeof(RouteAttribute), true)
                    || action.Controller.ControllerType.IsDefined(typeof(RouteAttribute), true))
                {
                    // 如果路由模板不为空，则直接使用
                    if (string.IsNullOrWhiteSpace(selectorModel.AttributeRouteModel.Template)
                        && !string.IsNullOrWhiteSpace(selectorModel.AttributeRouteModel.Name))
                    {
                        selectorModel.AttributeRouteModel.Template = selectorModel.AttributeRouteModel.Name;
                    }

                    var newTemplate = $"{(selectorModel.AttributeRouteModel.Template?.StartsWith("/") == true ? "/" : null)}{selectorModel.AttributeRouteModel.Template}";
                    // 处理多斜杠问题
                    newTemplate = Regex.Replace(newTemplate, @"\/{2,}", "/");
                    selectorModel.AttributeRouteModel.Template = newTemplate;

                    continue;
                }

                // 如果方法自定义路由模板且以 `/` 开头，则跳过
                if (!string.IsNullOrWhiteSpace(selectorModel.AttributeRouteModel.Template) && selectorModel.AttributeRouteModel.Template.StartsWith("/")) continue;
            }

            // 获取控制器路由模板
            var controllerRouteTemplate = action.Controller.Selectors[0].AttributeRouteModel?.Template;

            // 最终路由模板
            string finalTemplate;

            // 如果控制器有路由
            if (!string.IsNullOrEmpty(controllerRouteTemplate))
            {
                // ps:如果只有控制器路由 就只需要[action],否则会造成多次路由
                finalTemplate = string.IsNullOrWhiteSpace(action.ActionName)
                    || (action.Controller.Selectors[0].AttributeRouteModel?.Template?.Contains("[action]") ?? false)
                    ? null : (selectorModel?.AttributeRouteModel?.Template
                    ?? selectorModel?.AttributeRouteModel?.Name ?? "[action]");
            }
            else
            {
                // 默认动作路由
                var defaultActionRoute = CreateActionRouteTemplate(action);

                // 最终动作路由
                var finalActionRoute = defaultActionRoute ?? action.Selectors[0]?.AttributeRouteModel?.Template;

                // 获取到动作路由就使用控制器路由+动作路由
                if (defaultActionRoute != null)
                {
                    var defaultControllerRoute = CreateControllerRouteTemplate(action.Controller) ?? action.Controller.Selectors[0]?.AttributeRouteModel?.Template;
                    finalTemplate = $"{defaultControllerRoute}/{finalActionRoute}";
                }
                else
                {
                    // 否则直接使用动作路由
                    finalTemplate = $"/{finalActionRoute}";
                }
            }

            // 获取路由参数
            var parameterRoute = CreateParameterRouteTemplate(action);

            if (!string.IsNullOrEmpty(parameterRoute))
            {
                // 添加路由参数
                finalTemplate = $"{finalTemplate}/{parameterRoute}";
            }

            // 清理和规范化路由模板
            finalTemplate = Regex.Replace(finalTemplate, @"\/{2,}", "/");

            if (!_options.Routing.KeepParameterOriginalName)
            {
                // 应用命名规则
                if (_options.Routing.UseCamelCase)
                {
                    finalTemplate = finalTemplate.ToCamelCase();
                }
            }

            // 设置最终路由
            selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(finalTemplate));
        }
    }

    /// <summary>
    /// 标准化请求方式
    /// </summary>
    /// <param name="action"></param>
    private void NormalizeSelectorHttpMethod(ActionModel action)
    {
        var selectorModel = action.Selectors[0];

        // 跳过已配置请求谓词特性的配置
        if (selectorModel.ActionConstraints.Any(u => u is HttpMethodActionConstraint)) return;

        // 获取请求方式
        selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));
    }

    /// <summary>
    /// 构建控制器路由模板
    /// </summary>
    /// <param name="controller"></param>
    /// <returns></returns>
    private string CreateControllerRouteTemplate(ControllerModel controller)
    {
        var selectorModel = controller.Selectors[0];

        // 跳过已配置路由特性的配置
        if (selectorModel.AttributeRouteModel != null) return default;

        // 路由默认前缀
        var routePrefix = _options.Routing.ApiRoutePrefix;

        // 控制器名称
        var controllerName = controller.ControllerName;

        // 保持原名称
        if (_options.Routing.KeepParameterOriginalName) { return routePrefix + "/" + controllerName; }

        // 移除控制器名称中的后缀
        foreach (var suffix in _options.Routing.ControllerNameSuffixesToRemove)
        {
            if (controllerName.EndsWith(suffix))
            {
                controllerName = controllerName.Substring(0, controllerName.Length - suffix.Length);
                break;
            }
        }

        // 如果启用了复数控制器名称
        if (_options.Routing.PluralizeControllerNames)
        {
            controllerName += "s";
        }

        var routeTemplate = routePrefix + "/" + controllerName;
        return routeTemplate;
    }

    /// <summary>
    /// 构建动作路由模板，自动拼接参数名以保证唯一性
    /// </summary>
    /// <param name="action"></param>
    /// <returns></returns>
    private string CreateActionRouteTemplate(ActionModel action)
    {
        var actionName = action.ActionMethod.Name;

        if (_options.Routing.KeepParameterOriginalName)
        {
            return actionName;
        }

        // 检查同一控制器下是否有重载方法
        var overloads = action.Controller.Actions.Count(a => a.ActionMethod.Name == actionName);
        if (overloads > 1)
        {
            // 拼接参数名，保证唯一
            var paramNames = string.Join("_", action.Parameters.Select(p => p.ParameterName));
            if (!string.IsNullOrEmpty(paramNames))
            {
                actionName = $"{actionName}_by_{paramNames}";
            }
        }

        // 移除动作名称中的后缀
        foreach (var suffix in _options.Routing.ActionNameSuffixesToRemove)
        {
            if (actionName.EndsWith(suffix))
            {
                actionName = actionName.Substring(0, actionName.Length - suffix.Length);
                break;
            }
        }

        // 移除动作名称中的前缀
        foreach (var prefix in _options.Routing.ActionNamePrefixesToRemove)
        {
            if (actionName.StartsWith(prefix))
            {
                actionName = actionName.Substring(prefix.Length);
                break;
            }
        }

        return actionName;
    }

    /// <summary>
    /// 构建参数路由模板
    /// </summary>
    /// <param name="action">Action模型</param>
    /// <returns>参数路由模板</returns>
    private string CreateParameterRouteTemplate(ActionModel action)
    {
        if (action.Parameters.Count == 0) return default;

        // 参数集合
        var parameterTemplates = new List<string>();

        foreach (var parameter in action.Parameters)
        {
            // 如果参数已有绑定特性，则跳过
            if (parameter.BindingInfo != null) continue;

            // 获取参数类型和名称
            var parameterType = parameter.ParameterInfo.ParameterType;
            var parameterName = parameter.ParameterName;

            // 处理特殊类型参数
            if (parameterType.IsSpecialType()) continue;

            // 获取HTTP方法
            var httpMethod = GetHttpMethod(action);

            // 根据HTTP方法和参数类型确定绑定方式
            ConfigureParameterBinding(parameter, httpMethod, parameterType, parameterName);

            // 如果是路由参数，添加到模板中
            if (parameter.IsRouteParameter())
            {
                parameterTemplates.Add($"{{{parameterName}}}");
            }
        }

        // 组合参数路由模板
        return parameterTemplates.Count > 0 ? string.Join("/", parameterTemplates) : default;
    }

    /// <summary>
    /// 配置参数绑定
    /// </summary>
    private void ConfigureParameterBinding(ParameterModel parameter, string httpMethod, Type parameterType, string parameterName)
    {
        // 根据HTTP方法和参数类型确定绑定方式
        if (IsQueryMethod(httpMethod))
        {
            // GET、DELETE等请求默认使用Query绑定
            ConfigureQueryBinding(parameter, parameterName);
        }
        else if (IsSimpleType(parameterType))
        {
            // 简单类型默认使用Route绑定
            ConfigureRouteBinding(parameter, parameterName);
        }
        else
        {
            // 复杂类型默认使用Body绑定
            ConfigureBodyBinding(parameter);
        }
    }

    /// <summary>
    /// 判断是否为查询方法
    /// </summary>
    private bool IsQueryMethod(string httpMethod)
    {
        // 使用预先缓存的查询方法集合进行快速查找
        return _queryMethods.Contains(httpMethod);
    }

    /// <summary>
    /// 配置查询参数绑定
    /// </summary>
    private void ConfigureQueryBinding(ParameterModel parameter, string parameterName)
    {
        parameter.BindingInfo = new BindingInfo
        {
            BindingSource = BindingSource.Query,
            BinderModelName = parameterName
        };
    }

    /// <summary>
    /// 配置路由参数绑定
    /// </summary>
    private void ConfigureRouteBinding(ParameterModel parameter, string parameterName)
    {
        parameter.BindingInfo = new BindingInfo
        {
            BindingSource = BindingSource.Path,
            BinderModelName = parameterName
        };
    }

    /// <summary>
    /// 配置请求体参数绑定
    /// </summary>
    private void ConfigureBodyBinding(ParameterModel parameter)
    {
        parameter.BindingInfo = new BindingInfo
        {
            BindingSource = BindingSource.Body
        };
    }

    /// <summary>
    /// 获取HTTP方法
    /// </summary>
    private string GetHttpMethod(ActionModel action)
    {
        // 1. 优先从ActionConstraints获取
        var httpMethods = action.Selectors
            .SelectMany(temp => temp.ActionConstraints)
            .OfType<HttpMethodActionConstraint>()
            .SelectMany(temp => temp.HttpMethods)
            .ToList();
        if (httpMethods.Count > 0)
        {
            return httpMethods.First();
        }

        // 2. 再根据配置的HttpMethodMappings决定
        var actionName = action.ActionMethod.Name;
        foreach (var mapping in _options.Routing.HttpMethodMappings)
        {
            if (actionName.StartsWith(mapping.Key))
            {
                return mapping.Value;
            }
        }

        // 3. 最后默认POST
        return "POST";
    }

    /// <summary>
    /// 判断是否为简单类型
    /// </summary>
    private bool IsSimpleType(Type type)
    {
        // 使用缓存提高性能
        return _simpleTypeCache.GetOrAdd(type, t => 
        {
            return t.IsPrimitive
                || t == typeof(string)
                || t == typeof(DateTime)
                || t == typeof(DateTimeOffset)
                || t == typeof(TimeSpan)
                || t == typeof(Guid)
                || t == typeof(decimal)
                || t.IsEnum
                || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)
                    && IsSimpleType(t.GetGenericArguments()[0]));
        });
    }
}